package com.pmdb.core.data;

import java.util.Collections;
import java.util.Iterator;
import java.util.SortedSet;
import java.util.TreeSet;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

import org.apache.log4j.Logger;
import org.hibernate.annotations.Sort;
import org.hibernate.annotations.SortType;

//import javax.persistence.CascadeType;

@Entity
@Table(name = "T_MOVIE")
public class Movie implements Comparable<Movie> {
   
   private static final Logger LOG = Logger.getLogger(Movie.class);
   
   @Id @GeneratedValue
   @Column(name = "ID")
   private Long id;

   @Column(name = "ORIGINAL_TITLE")
   private String originalTitle;

   @Column(name = "RELEASED_YEAR")
   private int releasedYear;

   @Column(name = "PLOT")
   private String plot;

   @Column(name = "RATING")
   private int rating;

   @Column(name = "PERSONAL_RATING")
   private int personalRating;

   @Column(name = "COLOR")
   private boolean color; // B&W or Color

   @Column(name = "RUNTIME")
   private int runtime; // minutes

   @ManyToMany
   @JoinTable(
      name="MOVIE_COUNTRY",
      joinColumns={@JoinColumn(name="MOVIE_ID")},
      inverseJoinColumns={@JoinColumn(name="COUNTRY_ID")
      }
   )
   @Sort(type = SortType.NATURAL)
   private SortedSet<Country> countries;
   
   
   @ManyToMany
   @JoinTable(
      name="MOVIE_LANGUAGE",
      joinColumns={@JoinColumn(name="MOVIE_ID")},
      inverseJoinColumns={@JoinColumn(name="LANGUAGE_ID")
      }
   )
   @Sort(type = SortType.NATURAL)
   private SortedSet<Language> languages;
   
   
   @ManyToMany
   @JoinTable(
      name="MOVIE_SUBTITLE",
      joinColumns={@JoinColumn(name="MOVIE_ID")},
      inverseJoinColumns={@JoinColumn(name="SUBTITLE_ID")
      }
   )
   @Sort(type = SortType.NATURAL)
   private SortedSet<Language> subtitles;
   
   
   @ManyToMany
   @JoinTable(
      name="MOVIE_GENRE",
      joinColumns={@JoinColumn(name="MOVIE_ID")},
      inverseJoinColumns={@JoinColumn(name="GENRE_ID")
      }
   )
   @Sort(type = SortType.NATURAL)
   private SortedSet<Genre> genres;
   
   
   @ManyToMany
   @JoinTable(
      name="MOVIE_PRODUCER",
      joinColumns={@JoinColumn(name="MOVIE_ID")},
      inverseJoinColumns={@JoinColumn(name="PRODUCER_ID")
      }
   )
   @Sort(type = SortType.NATURAL)
   private SortedSet<Person> producers;
   
   
   @ManyToMany
   @JoinTable(
      name="MOVIE_DIRECTOR",
      joinColumns={@JoinColumn(name="MOVIE_ID")},
      inverseJoinColumns={@JoinColumn(name="DIRECTOR_ID")
      }
   )
   @Sort(type = SortType.NATURAL)
   private SortedSet<Person> directors;
   
   
   @ManyToMany
   @JoinTable(
      name="MOVIE_WRITER",
      joinColumns={@JoinColumn(name="MOVIE_ID")},
      inverseJoinColumns={@JoinColumn(name="WRITER_ID")
      }
   )
   @Sort(type = SortType.NATURAL)
   private SortedSet<Person> writers;
   
   
   @ManyToMany
   @JoinTable(
      name="MOVIE_ACTOR",
      joinColumns={@JoinColumn(name="MOVIE_ID")},
      inverseJoinColumns={@JoinColumn(name="ACTOR_ID")
      }
   )
   @Sort(type = SortType.NATURAL)
   private SortedSet<Person> actors;

   
   /*
   
   @OneToMany(cascade = CascadeType.ALL)
   @JoinColumn(name = "COUNTRY_ID")
   private Collection<String> aka;*/

   public Movie() {
      this.countries = new TreeSet<Country>();
      this.languages = new TreeSet<Language>();
      this.subtitles = new TreeSet<Language>();
      this.genres = new TreeSet<Genre>();
      this.producers = new TreeSet<Person>();
      this.directors = new TreeSet<Person>();
      this.writers = new TreeSet<Person>();
      this.actors = new TreeSet<Person>();
      LOG.debug("New movie!");
   }
   
   public Movie(String originalTitle, int releasedYear, String plot, int rating, int personalRating, boolean color, int runtime) {
      this();
      this.originalTitle = originalTitle;
      this.releasedYear = releasedYear;
      this.plot = plot;
      this.rating = rating;
      this.personalRating = personalRating;
      this.color = color;
      this.runtime = runtime;
   }

   public Long getId() {
      return id;
   }

   public void setId(Long id) {
      this.id = id;
   }

   public String getOriginalTitle() {
      return originalTitle;
   }

   public void setOriginalTitle(String originalTitle) {
      this.originalTitle = originalTitle;
   }

   public int getReleasedYear() {
      return releasedYear;
   }

   public void setReleasedYear(int releasedYear) {
      this.releasedYear = releasedYear;
   }

   public String getPlot() {
      return plot;
   }

   public void setPlot(String plot) {
      this.plot = plot;
   }

   public int getRating() {
      return rating;
   }

   public void setRating(int rating) {
      this.rating = rating;
   }

   public int getPersonalRating() {
      return personalRating;
   }

   public void setPersonalRating(int personalRating) {
      this.personalRating = personalRating;
   }

   public boolean isColor() {
      return color;
   }

   public void setColor(boolean color) {
      this.color = color;
   }

   public int getRuntime() {
      return runtime;
   }

   public void setRuntime(int runtime) {
      this.runtime = runtime;
   }
   
   
   /***** COUNTRY *****/

   public SortedSet<Country> getCountries() {
      return Collections.unmodifiableSortedSet(this.countries);
   }
   
   public void addCountry(Country country) {
      this.addCountry(country, true);
   }
   
   public void addCountry(Country country, boolean linkBack) {
      if(country == null) {
         throw new IllegalArgumentException("Null country!");
      }
      this.countries.add(country);
      if(linkBack) {
         country.addMovie(this, false);
      }
   }
   
   public void removeCountry(Country country) {
      this.removeCountry(country, true);
   }
   
   public void removeCountry(Country country, boolean removeLinkBack) {
      if(country == null) {
         throw new IllegalArgumentException("Null country!");
      }
      this.countries.remove(country);
      if(removeLinkBack) {
         country.removeMovie(this, false);
      }
   }
   
   
   /***** LANGUAGE *****/

   public SortedSet<Language> getLanguages() {
      return Collections.unmodifiableSortedSet(this.languages);
   }
   
   public void addAudioLanguage(Language language) {
      this.addAudioLanguage(language, true);
   }
   
   public void addAudioLanguage(Language language, boolean linkBack) {
      if(language == null) {
         throw new IllegalArgumentException("Null language!");
      }
      this.languages.add(language);
      if(linkBack) {
         language.addMovieForAudio(this, false);
      }
   }
   
   public void removeAudioLanguage(Language language) {
      this.removeAudioLanguage(language, true);
   }
   
   public void removeAudioLanguage(Language language, boolean removeLinkBack) {
      if(language == null) {
         throw new IllegalArgumentException("Null language!");
      }
      this.languages.remove(language);
      if(removeLinkBack) {
         language.removeMovieForAudio(this, false);
      }
   }
   
   
   /***** SUBTITLE *****/

   public SortedSet<Language> getSubtitles() {
      return Collections.unmodifiableSortedSet(this.subtitles);
   }
   
   public void addSubtitleLanguage(Language subtitle) {
      this.addSubtitleLanguage(subtitle, true);
   }
   
   public void addSubtitleLanguage(Language subtitle, boolean linkBack) {
      if(subtitle == null) {
         throw new IllegalArgumentException("Null subtitle!");
      }
      this.subtitles.add(subtitle);
      if(linkBack) {
         subtitle.addMovieForSubtitle(this, false);
      }
   }
   
   public void removeSubtitleLanguage(Language subtitle) {
      this.removeSubtitleLanguage(subtitle, true);
   }
   
   public void removeSubtitleLanguage(Language subtitle, boolean removeLinkBack) {
      if(subtitle == null) {
         throw new IllegalArgumentException("Null subtitle!");
      }
      this.subtitles.remove(subtitle);
      if(removeLinkBack) {
         subtitle.removeMovieForSubtitle(this, false);
      }
   }
   
   
   /***** GENRE *****/

   public SortedSet<Genre> getGenres() {
      return Collections.unmodifiableSortedSet(this.genres);
   }
   
   public void addGenre(Genre genre) {
      this.addGenre(genre, true);
   }
   
   public void addGenre(Genre genre, boolean linkBack) {
      if(genre == null) {
         throw new IllegalArgumentException("Null genre!");
      }
      this.genres.add(genre);
      if(linkBack) {
         genre.addMovie(this, false);
      }
   }
   
   public void removeGenre(Genre genre) {
      this.removeGenre(genre, true);
   }
   
   public void removeGenre(Genre genre, boolean removeLinkBack) {
      if(genre == null) {
         throw new IllegalArgumentException("Null genre!");
      }
      this.genres.remove(genre);
      if(removeLinkBack) {
         genre.removeMovie(this, false);
      }
   }
   
   
   /***** PRODUCER *****/

   public SortedSet<Person> getProducers() {
      return Collections.unmodifiableSortedSet(this.producers);
   }
   
   public void addProducer(Person person) {
      this.addProducer(person, true);
   }
   
   public void addProducer(Person person, boolean linkBack) {
      if(person == null) {
         throw new IllegalArgumentException("Null person!");
      }
      this.producers.add(person);
      if(linkBack) {
         person.addMovieAsProducer(this, false);
      }
   }
   
   public void removeProducer(Person person) {
      this.removeProducer(person, true);
   }
   
   public void removeProducer(Person person, boolean removeLinkBack) {
      if(person == null) {
         throw new IllegalArgumentException("Null person!");
      }
      this.producers.remove(person);
      if(removeLinkBack) {
         person.removeMovieAsProducer(this, false);
      }
   }
   
   
   /***** DIRECTOR *****/

   public SortedSet<Person> getDirectors() {
      return Collections.unmodifiableSortedSet(this.directors);
   }
   
   public void addDirector(Person person) {
      this.addDirector(person, true);
   }
   
   public void addDirector(Person person, boolean linkBack) {
      if(person == null) {
         throw new IllegalArgumentException("Null person!");
      }
      this.directors.add(person);
      if(linkBack) {
         person.addMovieAsDirector(this, false);
      }
   }
   
   public void removeDirector(Person person) {
      this.removeDirector(person, true);
   }
   
   public void removeDirector(Person person, boolean removeLinkBack) {
      if(person == null) {
         throw new IllegalArgumentException("Null person!");
      }
      this.directors.remove(person);
      if(removeLinkBack) {
         person.removeMovieAsDirector(this, false);
      }
   }
   
   
   /***** WRITER *****/

   public SortedSet<Person> getWriters() {
      return Collections.unmodifiableSortedSet(this.writers);
   }
   
   public void addWriter(Person person) {
      this.addWriter(person, true);
   }
   
   public void addWriter(Person person, boolean linkBack) {
      if(person == null) {
         throw new IllegalArgumentException("Null person!");
      }
      this.writers.add(person);
      if(linkBack) {
         person.addMovieAsWriter(this, false);
      }
   }
   
   public void removeWriter(Person person) {
      this.removeWriter(person, true);
   }
   
   public void removeWriter(Person person, boolean removeLinkBack) {
      if(person == null) {
         throw new IllegalArgumentException("Null person!");
      }
      this.writers.remove(person);
      if(removeLinkBack) {
         person.removeMovieAsWriter(this, false);
      }
   }
   
   
   /***** ACTOR *****/

   public SortedSet<Person> getActors() {
      return Collections.unmodifiableSortedSet(this.actors);
   }
   
   public void addActor(Person person) {
      this.addActor(person, true);
   }
   
   public void addActor(Person person, boolean linkBack) {
      if(person == null) {
         throw new IllegalArgumentException("Null person!");
      }
      this.actors.add(person);
      if(linkBack) {
         person.addMovieAsActor(this, false);
      }
   }
   
   public void removeActor(Person person) {
      this.removeActor(person, true);
   }
   
   public void removeActor(Person person, boolean removeLinkBack) {
      if(person == null) {
         throw new IllegalArgumentException("Null person!");
      }
      this.actors.remove(person);
      if(removeLinkBack) {
         person.removeMovieAsActor(this, false);
      }
   }

   /*public Set<String> getAka() {
      return aka;
   }

   public void setAka(Set<String> aka) {
      this.aka = aka;
   }*/

   @Override
   public String toString() {
      StringBuilder strBld = new StringBuilder();
      strBld.append("Movie={[");
      strBld.append(id);
      strBld.append("] ");
      strBld.append(originalTitle);
      strBld.append("(");
      strBld.append(releasedYear);
      strBld.append("):");
      Iterator<Person> iter = directors.iterator();
      if(iter.hasNext()) {
         Person director = iter.next();
         strBld.append(director.getFullName());
         while(iter.hasNext()) {
            strBld.append(", ");
            strBld.append(director.getFullName());
         }
      }
      
      strBld.append("}");
      
      return strBld.toString();
   }

   @Override
   public int compareTo(Movie comp) {
      return this.originalTitle.compareTo(comp.originalTitle);
   }
}
